﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Clandestine;
using Slick.MapEditor;

namespace Slick
{
    public partial class TilesControl : UserControl
    {
        public Map Map;
        TilesetsChangeEvent tce;
        Rectangle selection = new Rectangle();
        int zoom = 1;
        Bitmap cachedZoomedBitmap;
        int downX = -1; // for mouse down event
        int downY = -1;

        // GridWidth/GridHeight that take into account our Zoom!
        public int GridSize
        {
            get { return Program.GridSize * zoom; }
        }


        public TilesControl()
        {
            InitializeComponent();

            cmbTileset.Text = "(none)";
            tce = new TilesetsChangeEvent(RepopulateTilesetList);
            Tilesets.OnTilesetsChange += tce;
            RepopulateTilesetList();
        }

        public void Initialise(Map map)
        {
            this.Map = map;
        }

        private void RepopulateTilesetList()
        {
            lock (cmbTileset)
            {
                lock (Tilesets.LoadedTilesets)
                {
                    string current = cmbTileset.Text;

                    cmbTileset.Items.Clear();
                    cmbTileset.Items.Add("(none)");
                    foreach (string s in Tilesets.LoadedTilesets)
                        cmbTileset.Items.Add(s);

                    if (!cmbTileset.Items.Contains(current))
                        cmbTileset.Text = "(none)";
                    else
                        cmbTileset.Text = current;
                }
            }
        }

        private void DrawBox(MouseEventArgs e)
        {
            // (If the user selected the rect backwards, note that we flip the 
            //  appropriate coords. That's what the dx,dy,ux,uy nonsense is about.)
            int dx, dy, ux, uy;
            dx = (downX <= e.X ? downX : e.X);
            ux = (downX <= e.X ? e.X : downX);
            dy = (downY <= e.Y ? downY : e.Y);
            uy = (downY <= e.Y ? e.Y : downY);

            // (after possible flipping:)
            // start coord should round to the top-left tile from the coordinate clicked,
            // end coord should round to the bottom-right tile from the coordinate clicked.
            int sx, sy, fx, fy;
            sx = dx - (dx % this.GridSize);
            fx = ux + (this.GridSize - (ux % this.GridSize));
            sy = dy - (dy % this.GridSize);
            fy = uy + (this.GridSize - (uy % this.GridSize));

            DrawBox(sx, sy, fx - sx, fy - sy); // New box
        }
        private void DrawBox(Rectangle r)
        {
            DrawBox(r.X, r.Y, r.Width, r.Height);
        }
        private void DrawBox(int x, int y, int width, int height)
        {
            if (cachedZoomedBitmap == null)
                return; // nothing loaded yet..

            // Draw box around selected area.
            Bitmap bmpTileset = (Bitmap)cachedZoomedBitmap.Clone();
            System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmpTileset);
            Pen p = new Pen(Brushes.Black, 1);

            g.DrawRectangle(p, x, y, width, height);
            g.DrawRectangle(p, selection); // hax. We might have just drawn this, but if we have, it wont matter. This is here incase the DrawBox() is for a hovering preview, we dont want the current selection to disappear.
            g.Flush(System.Drawing.Drawing2D.FlushIntention.Sync);
            g.Dispose();
            if (picTileset.Image != cachedZoomedBitmap)
                picTileset.Image.Dispose();
            picTileset.Image = bmpTileset;
            // panel1.Refresh();
        }

        private void cmbTileset_SelectedIndexChanged(object sender, EventArgs e)
        {
            lock (cmbTileset)
            {
                Tilesets.SelectTiles(new List<List<string>>());

                if (cmbTileset.SelectedIndex == 0) // i.e. (none)
                {
                    picTileset.Image = null;
                    return;
                }

                // Otherwise...
                picTileset.Image = ZoomImage(new Bitmap(Texture.GetTexture(cmbTileset.Text).Bitmap));
            }
        }

        private Bitmap ZoomImage(Bitmap oldBmp)
        {
            Bitmap newBmp = new Bitmap(oldBmp.Width * zoom, oldBmp.Height * zoom);
            System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(newBmp);
            g.DrawImage(oldBmp, new Rectangle(0, 0, newBmp.Width, newBmp.Height));
            g.Flush();
            g.Dispose();
            cachedZoomedBitmap = newBmp;
            // oldBmp.Dispose();
            return newBmp;
        }

        private void picTileset_MouseDown(object sender, MouseEventArgs e)
        {
            picTileset.Image = new Bitmap(Texture.GetTexture(cmbTileset.Text).Bitmap);

            if (e.Button == MouseButtons.Left)
            {
                downX = e.X;
                downY = e.Y;
            }
            else
            {
                downX = -1;
                downY = -1;

                // Clear tile selection.
                // (Tilesets automatically does the logic to determine if we're the active
                // tileset window or not.)
                Tilesets.SelectTiles(new List<List<string>>());
            }
        }

        private string getTileNameByCoords(int tilex, int tiley)
        {
            // note (picTileset.Image.Width / this.GridSize) is integer division
            // .'. always rounds down
            return (cmbTileset.Text + "!" + (tiley * (picTileset.Image.Width / this.GridSize) + tilex).ToString());
        }

        private void picTileset_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left || downX == -1 || downY == -1)
                return;
            if (e.X > picTileset.Image.Width || e.Y > picTileset.Image.Height)
                return;
            if (picTileset.Image == null)
                return;

            // (If the user selected the rect backwards, note that we flip the 
            //  appropriate coords. That's what the dx,dy,ux,uy nonsense is about.)
            int dx, dy, ux, uy;
            dx = (downX <= e.X ? downX : e.X);
            ux = (downX <= e.X ? e.X : downX);
            dy = (downY <= e.Y ? downY : e.Y);
            uy = (downY <= e.Y ? e.Y : downY);

            // (after possible flipping:)
            // start coord should round to the top-left tile from the coordinate clicked,
            // end coord should round to the bottom-right tile from the coordinate clicked.
            int sx, sy, fx, fy;
            sx = dx - (dx % this.GridSize);
            fx = ux + (this.GridSize - (ux % this.GridSize));
            sy = dy - (dy % this.GridSize);
            fy = uy + (this.GridSize - (uy % this.GridSize));

            // Remember selection
            selection = new Rectangle(sx, sy, fx - sx, fy - sy);

            // Draw box around selection
            DrawBox(sx, sy, fx - sx, fy - sy);

            // Build list of selected tiles...
            List<List<string>> selectedTiles = new List<List<string>>();

            for (int tileyoffset = 0; tileyoffset < (fy - sy) / this.GridSize; tileyoffset++)
            {
                selectedTiles.Add(new List<string>());

                for (int tilexoffset = 0; tilexoffset < (fx - sx) / this.GridSize; tilexoffset++)
                    selectedTiles[tileyoffset].Add(
                        getTileNameByCoords(
                        (sx / this.GridSize) + tilexoffset,
                        (sy / this.GridSize) + tileyoffset));
            }

            Tilesets.SelectTiles(selectedTiles);
        }

        private void picTileset_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
                DrawBox(e);
            else
            {
                int sx = e.X - (e.X % this.GridSize);
                int fx = e.X + (this.GridSize - (e.X % this.GridSize));
                int sy = e.Y - (e.Y % this.GridSize);
                int fy = e.Y + (this.GridSize - (e.Y % this.GridSize));
                DrawBox(sx, sy, fx - sx, fy - sy); // Draw hovering selection
            }
        }
    }
}
